// #include <iostream>
// #include <unistd.h>   // For pipe(), fork(), read(), write(), close()
// #include <sys/wait.h> // For wait()
// #include <string>
// #include <cstring> // For strlen()

// const int BUFFER_SIZE = 256;

// int main()
// {
//     int pipefd[2]; // pipefd[0] for read, pipefd[1] for write

//     // 1. 创建管道
//     if (pipe(pipefd) == -1)
//     {
//         perror("pipe"); // 错误处理
//         return 1;
//     }

//     // 2. 创建子进程
//     pid_t pid = fork();

//     if (pid == -1)
//     {
//         perror("fork"); // 错误处理
//         return 1;
//     }

//     if (pid == 0)
//     {                     // 子进程 (消费者：从管道读取并输出到屏幕)
//         close(pipefd[1]); // 关闭子进程的写入端，因为子进程只从管道读取

//         char buffer[BUFFER_SIZE];
//         ssize_t bytes_read;

//         std::cout << "Child process: Ready to read from pipe and output to screen." << std::endl;

//         // pipefd[0] 为读端
//         while ((bytes_read = read(pipefd[0], buffer, BUFFER_SIZE - 1)) > 0)
//         {
//             buffer[bytes_read] = '\0';                         // 确保字符串以空字符结尾
//             std::cout << "Child process received: " << buffer; // 假设数据包含换行符
//             // 如果你希望每收到一条就加一个换行，可以用：
//             // std::cout << "Child process received: " << buffer << std::endl;
//         }

//         if (bytes_read == -1)
//         {
//             perror("read");
//         }

//         close(pipefd[0]); // 关闭子进程的读取端
//         std::cout << "Child process: Pipe read end closed. Exiting." << std::endl;
//         _exit(0); // 子进程退出
//     }
//     else
//     {
//         // 父进程 (生产者：从键盘读取并写入管道)
//         close(pipefd[0]); // 关闭父进程的读取端，因为父进程只向管道写入，且进程的文件描述是独立的

//         std::string line;
//         std::cout << "Parent process: Enter text (type 'exit' to quit):" << std::endl;

//         while (true)
//         {
//             std::getline(std::cin, line); // 从键盘读取一行

//             if (line == "exit")
//             {
//                 // 如果需要确保子进程收到退出信号，可以发送特定字符串
//                 // 或者直接关闭写入端，让子进程读到EOF
//                 write(pipefd[1], "exit\n", 5);
//                 break;
//             }

//             // 写入管道。如果行尾没有换行符，最好加上，以便子进程按行读取
//             std::string data_to_write = line + "\n";
//             if (write(pipefd[1], data_to_write.c_str(), data_to_write.length()) == -1)
//             {
//                 perror("write");
//                 break;
//             }
//         }

//         close(pipefd[1]); // 关闭父进程的写入端，发出EOF信号给子进程
//         std::cout << "Parent process: Pipe write end closed. Waiting for child..." << std::endl;

//         wait(NULL); // 等待子进程结束
//         std::cout << "Parent process: Child process finished. Exiting." << std::endl;
//     }

//     return 0;
// }

// #include <stdio.h>
// #include <stdlib.h>
// #include <sys/ipc.h>
// #include <sys/sem.h>
// #include <unistd.h>
// #include <sys/wait.h> // 用于 wait() 函数

// #if defined(__linux__) || defined(__unix__)
// // 为 semctl 函数显式定义 union semun。
// // 这在 Linux/Unix-like 系统上，对于现代 GCC 编译器通常是必需的。
// union semun {
//     int val;                /* 用于 SETVAL 命令的值 */
//     struct semid_ds *buf;   /* 用于 IPC_STAT、IPC_SET 命令的缓冲区 */
//     unsigned short *array;  /* 用于 GETALL、SETALL 命令的数组 */
//     struct seminfo *__buf;  /* 用于 IPC_INFO 命令的缓冲区 (Linux 特有) */
// };
// #endif
// int main()
// {
//     // 生成一个唯一的key，用于标识信号量集
//     // 参数1: 文件路径("/tmp")，参数2: 项目ID('A')
//     key_t key = ftok("/tmp", 'A');

//     // 创建一个包含1个信号量的信号量集
//     // 参数1: key，参数2: 信号量数量(1)，参数3: 权限(IPC_CREAT | 0666)
//     int semid = semget(key, 1, IPC_CREAT | 0666);

//     // 定义一个union semun，用于设置信号量值
//     union semun arg;
//     // 初始化信号量值为0，表示资源不可用
//     arg.val = 0;

//     // 设置信号量集的第一个信号量的值
//     // 参数1: semid，参数2: 信号量索引(0)，参数3: 命令(SETVAL)，参数4: arg
//     semctl(semid, 0, SETVAL, arg);

//     // 创建子进程
//     pid_t pid = fork();
//     if (pid == 0) {
//         // 子进程
//         printf("子进程等待信号量...");
//         // 定义P操作结构体
//         // 参数1: 信号量索引(0)，参数2: 操作值(-1表示P操作)，参数3: 标志(0)
//         struct sembuf p = {0, -1, 0};
//         // 执行P操作(等待信号量)
//         semop(semid, &p, 1);
//         printf("子进程获得信号量，继续执行");
//         exit(0);
//     } else {
//         // 父进程
//         sleep(2); // 等待2秒，确保子进程先执行到P操作
//         printf("父进程释放信号量...
// ");
//         // 定义V操作结构体
//         // 参数1: 信号量索引(0)，参数2: 操作值(1表示V操作)，参数3: 标志(0)
//         struct sembuf v = {0, 1, 0};
//         // 执行V操作(释放信号量)
//         semop(semid, &v, 1);
//         // 等待子进程结束
//         wait(NULL);
//         // 删除信号量集
//         semctl(semid, 0, IPC_RMID);
//     }
//     return 0;
// }

#include <stdio.h>    // 包含标准输入输出库，用于 printf, perror 等函数
#include <stdlib.h>   // 包含标准库，用于 exit 函数
#include <sys/ipc.h>  // 包含 IPC（进程间通信）相关定义，如 key_t, IPC_CREAT, IPC_RMID
#include <sys/sem.h>  // 包含 System V 信号量相关函数和结构体，如 semget, semctl, semop, struct sembuf
#include <unistd.h>   // 包含 POSIX 操作系统 API，如 fork, sleep, pid_t
#include <sys/wait.h> // 包含进程等待函数，用于 wait()

// --- 显式定义 union semun ---
// union semun 是一个用于 semctl() 函数的联合体，特别是在 SETVAL 命令中需要用到。
// 它不是 POSIX 标准的一部分，但在 System V IPC 中广泛使用。
// 现代 GCC 编译器在某些系统上可能不自动定义它，因此为了代码的兼容性和健壮性，
// 最好显式地定义它。这个定义通常放在所有头文件包含之后。
#if defined(__linux__) || defined(__unix__)
union semun
{
    int val;               /* 用于 SETVAL 命令，设置信号量的值 */
    struct semid_ds *buf;  /* 用于 IPC_STAT 和 IPC_SET 命令，获取/设置信号量集的属性 */
    unsigned short *array; /* 用于 GETALL 和 SETALL 命令，获取/设置信号量集中的所有信号量值 */
    struct seminfo *__buf; /* 用于 IPC_INFO 命令，获取信号量系统限制信息 (Linux 特有) */
};
#endif
// --- union semun 定义结束 ---

int main()
{
    // 1. 生成 IPC key (IPC 键值)
    // ftok() 函数根据一个现有的文件路径（例如 "/tmp"）和一个整数（称为“项目 ID”，这里是 'A'）
    // 生成一个系统唯一的键值 (key_t 类型)。
    // 这个键值是用于在系统中唯一标识一个 IPC 资源（如信号量集、消息队列或共享内存）的“名字”。
    key_t key = ftok("/tmp", 'A');
    if (key == -1)
    {
        // 如果 ftok 失败，通常是文件路径不存在或没有权限。
        perror("ftok 失败"); // 打印错误信息
        return EXIT_FAILURE; // 返回失败状态
    }

    // 2. 创建或获取一个信号量集
    // semget(key, nsems, semflg) 函数用于获取一个信号量集的 ID (semid)。
    // 参数1: key，上一步生成的键值。
    // 参数2: nsems，信号量集中包含的信号量数量。这里我们只需要一个信号量，所以是 1。
    // 参数3: semflg，标志位和权限。
    //    IPC_CREAT: 如果信号量集不存在，则创建它。
    //    0666: 信号量集的访问权限，表示所有者、组用户和其他用户都有读写权限。
    int semid = semget(key, 1, IPC_CREAT | 0666);
    if (semid == -1)
    {
        // 如果 semget 失败，可能是权限问题，或者系统资源不足。
        perror("semget 失败");
        return EXIT_FAILURE;
    }

    // 3. 初始化信号量的值
    // 定义一个 union semun 类型的变量 arg，用于向 semctl 函数传递参数。
    union semun arg;
    // 将 arg.val 成员设置为 0。
    // 对于信号量，值为 0 通常表示资源当前不可用（或互斥锁被占用）。
    arg.val = 0;

    // semctl(semid, semnum, cmd, arg) 函数用于对信号量进行控制操作。
    // 参数1: semid，要操作的信号量集的 ID。
    // 参数2: semnum，信号量集中要操作的信号量索引（从 0 开始）。这里是第 0 个信号量。
    // 参数3: cmd，要执行的命令。SETVAL 表示设置指定信号量的值。
    // 参数4: arg，用于传递命令参数的联合体。在这里它包含了要设置的信号量值。
    if (semctl(semid, 0, SETVAL, arg) == -1)
    {
        // 如果 semctl 失败，打印错误信息。
        perror("semctl SETVAL 失败");
        // 如果信号量初始化失败，应该立即删除它，防止资源泄露。
        semctl(semid, 0, IPC_RMID);
        return EXIT_FAILURE;
    }

    // 4. 创建子进程
    // fork() 函数创建一个新的子进程。
    // 在父进程中，fork() 返回子进程的 PID (进程 ID)。
    // 在子进程中，fork() 返回 0。
    // 如果 fork() 失败，则返回 -1。
    pid_t pid = fork();

    if (pid == -1)
    {
        // 如果 fork 失败，通常是系统资源限制。
        perror("fork 失败");
        // 同样，如果进程创建失败，清理已创建的信号量。
        semctl(semid, 0, IPC_RMID);
        return EXIT_FAILURE;
    }

    if (pid == 0)
    {
        // --- 这是子进程执行的代码块 ---
        printf("子进程等待信号量..."); // 提示子进程正在等待
        // 定义一个 struct sembuf 结构体，用于描述 P 操作（等待操作）。
        // struct sembuf 结构体成员：
        //   sem_num: 信号量在集中的索引 (这里是 0)。
        //   sem_op: 操作值。-1 表示 P 操作 (将信号量值减 1)。
        //           如果信号量值变为负数，进程会阻塞，直到信号量值变为非负。
        //   sem_flg: 操作标志。0 表示默认行为（阻塞）。
        struct sembuf p_op = {0, -1, 0};

        // semop(semid, sops, nsops) 函数用于执行信号量操作。
        // 参数1: semid，要操作的信号量集的 ID。
        // 参数2: &p_op，指向 struct sembuf 结构体的指针（可以是一个结构体数组）。
        // 参数3: 1，表示要执行的操作数量（这里只执行一个 P 操作）。
        if (semop(semid, &p_op, 1) == -1)
        {
            // 如果 semop 失败，打印错误信息。
            perror("子进程 semop P 操作失败");
            // 子进程出错时，直接退出。
            exit(EXIT_FAILURE);
        }
        printf("子进程获得信号量，继续执行"); // 提示子进程已获得信号量

        // 子进程完成任务后退出。EXIT_SUCCESS 是一个表示成功退出的宏。
        exit(EXIT_SUCCESS);
    }
    else
    {
        // --- 这是父进程执行的代码块 ---
        printf("父进程：休眠 2 秒钟后释放信号量...\n");
        // sleep(2) 使父进程暂停执行 2 秒。
        // 这样做是为了确保子进程有机会先执行，并尝试执行 P 操作（从而阻塞）。
        sleep(2);

        printf("父进程：释放信号量...\n\n"); // 提示父进程将要释放信号量
        // 定义一个 struct sembuf 结构体，用于描述 V 操作（释放操作）。
        // sem_num: 0。
        // sem_op: 1 表示 V 操作 (将信号量值加 1)。
        //         如果当前有进程因等待此信号量而阻塞，其中一个将被唤醒。
        // sem_flg: 0。
        struct sembuf v_op = {0, 1, 0};

        // 执行 V 操作（释放信号量）。
        if (semop(semid, &v_op, 1) == -1)
        {
            perror("父进程 semop V 操作失败");
            // 即使 V 操作失败，也应该继续等待子进程并尝试清理 IPC 资源。
        }

        // wait(NULL) 函数用于父进程等待其任一子进程终止。
        // 如果子进程尚未终止，父进程会在此处阻塞。
        // NULL 参数表示不关心子进程的退出状态。
        if (wait(NULL) == -1)
        {                                              
        }
        printf("父进程：子进程已结束。\n");

        // 5. 删除信号量集
        // semctl(semid, semnum, cmd) 函数用于删除 IPC 资源。
        // semid: 要删除的信号量集的 ID。
        // 0: 对于 IPC_RMID 命令，这个参数通常是 0。
        // IPC_RMID: 删除 IPC 资源的命令。这会从系统中移除信号量集，释放其占用的资源。
        if (semctl(semid, 0, IPC_RMID) == -1)
        {
            perror("semctl IPC_RMID 失败");
        }
        printf("父进程：信号量已清理。\n");
    }

    // 程序成功退出。
    return EXIT_SUCCESS;
}